package lu.uni.adtool.domains;

import java.util.HashMap;
import java.util.Vector;

import lu.uni.adtool.adtree.ADTNode;
import lu.uni.adtool.adtree.Node;

/**
 * Evaluate value of each node for given domain and value assignement map.
 * 
 * @author Piot Kordy
 */
public class Evaluator<Type>
{
  private Domain<Type> domain;
  private HashMap<ADTNode,Type> resultMap;
  /**
   * Constructs a new instance.
   * @param newDomain domain.
   */
  public Evaluator(final Domain<Type> newDomain)
  {
    resultMap=null;
    this.domain = newDomain;
  }

  /**
   * Returns previously calculated value for a given node.
   * 
   * @param node a node
   * @return value at a node
   */
  public final Type getValue(final ADTNode node)
  {
    if (resultMap == null || node == null) {
      return null;
    }
    else {
      return resultMap.get(node);
    }
  }
  /**
   * Do bottom up evaluation.
   * 
   * @param root node from which we do evaluation
   * @param newmap mapping between node names and values.
   * @return true if evaluation was successful.
   */
  public final boolean reevaluate(final ADTNode root,
      final ValueAssignement<Type> newProMap, final ValueAssignement<Type> newOppMap)
  {
    if (newProMap == null||root == null || newOppMap == null) {
      return false;
    }
    resultMap = new HashMap<ADTNode,Type>();
    evaluate(root,newProMap,newOppMap);
    return true;
  }

  /**
   * Do bottom up evaluation.
   * 
   * @param root
   * @return
   */
  private Type evaluate(final ADTNode root,ValueAssignement<Type> proMap,
      final ValueAssignement<Type> oppMap)
  {
    Type result = null;
    //leaf
    if (root.getType() == ADTNode.Type.LEAFO){
      resultMap.put(root,oppMap.get(root.getName()));
      return oppMap.get(root.getName());
    }
    else if(root.getType() == ADTNode.Type.LEAFP) {
      resultMap.put(root,proMap.get(root.getName()));
      return proMap.get(root.getName());
    }
    else {
      final Vector<Node> children = root.getChildren();
      if (children == null) {
        System.err.println("Error in bottom up evaluation - "
            + "non leaf node without children");
        return null;
      }
      if (children.size() == 0) {
        System.err.println("Error in bottom up evaluation - "
            + "non leaf node without children");
        return null;
      }
      result = null;
      switch (root.getType()) {
        case OO:
          for (Node c:children){
            if(result == null){
              result = evaluate((ADTNode) children.elementAt(0),proMap,oppMap);
            }
            else{
              result = domain.oo(result,evaluate((ADTNode) c,proMap,oppMap));
            }
          }
          break;
        case AO:
          for (Node c:children){
            if(result == null){
              result = evaluate((ADTNode) children.elementAt(0),proMap,oppMap);
            }
            else{
              result = domain.ao(result,evaluate((ADTNode) c,proMap,oppMap));
            }
          }
          break;
        case OP:
          for (Node c:children){
            if(result == null){
              result = evaluate((ADTNode) children.elementAt(0),proMap,oppMap);
            }
            else{
              result = domain.op(result,evaluate((ADTNode) c,proMap,oppMap));
            }
          }
          break;
        case AP:
          for (Node c:children){
            if(result == null){
              result = evaluate((ADTNode) children.elementAt(0),proMap,oppMap);
            }
            else{
              result = domain.ap(result,evaluate((ADTNode) c,proMap,oppMap));
            }
          }
          break;
        case CO:
          if (children.size() != 2){
            System.err.println("Error in bottom up evaluation - "
                +" counter node with more than 2 children.");
            return null;
          }
          result = domain.co(
                evaluate((ADTNode) children.elementAt(0), proMap, oppMap),
              evaluate((ADTNode) children.elementAt(1),proMap,oppMap));
          break;
        case CP:
          if (children.size() != 2){
            System.err.println("Error in bottom up evaluation - "
                + "counter node with more than 2 children.");
            return null;
          }
          result = domain.cp(
              evaluate((ADTNode) children.elementAt(0), proMap, oppMap),
              evaluate((ADTNode) children.elementAt(1), proMap, oppMap));
          break;
        default:
          System.err.println("Error in bottom up evaluation - "
              + "Unknown node type.");
          return null;
      }
    }
    resultMap.put(root,result);
    return result;
  }
}

